#pragma once

#include <dlfcn.h>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#define   U8    unsigned char
#define   U32   unsigned int
#define   U16   unsigned short

#define JLINKARM_HOSTIF_USB   (1 << 0)
#define JLINKARM_HOSTIF_IP    (1 << 1)
#define JLINK_UNSECURE_DIALOG_CB_FUNC  JLINK_HOOK_DIALOG_UNSECURE  // Define for backward compatability
typedef int JLINK_HOOK_DIALOG_UNSECURE       (const char* sTitle, const char* sMsg, uint32_t Flags);
typedef void            JLINKARM_LOG                    (const char* sErr);

namespace rm_serial_driver {
// RTT Commands
typedef enum {
    RTT_COMMAND_START = 0,
    RTT_COMMAND_STOP = 1,
    RTT_COMMAND_GETDESC = 2,
    RTT_COMMAND_GETNUMBUF = 3,
    RTT_COMMAND_GETSTAT = 4
} JLinkRTTCommand;

// RTT Direction
typedef enum {
    RTT_DIRECTION_UP = 0,
    RTT_DIRECTION_DOWN = 1
} JLinkRTTDirection;

// RTT Errors
typedef enum {
    RTT_ERROR_CONTROL_BLOCK_NOT_FOUND = -2
} JLinkRTTErrors;

typedef struct {
    uint32_t       SerialNumber;             // This is the serial number reported in the discovery process, which is the "true serial number" for newer J-Links and 123456 for older J-Links.
    unsigned  Connection;               // Either JLINKARM_HOSTIF_USB = 1 or JLINKARM_HOSTIF_IP = 2
    uint32_t      USBAddr;                  // USB Addr. Default is 0, values of 0..3 are permitted (Only filled if for J-Links connected via USB)
    uint8_t        aIPAddr[16];              // IP Addr. in case emulator is connected via IP. For IP4 (current version), only the first 4 bytes are used.
    int       Time;                     // J-Link via IP only: Time period [ms] after which we have received the UDP discover answer from emulator (-1 if emulator is connected over USB)
    uint64_t       Time_us;                  // J-Link via IP only: Time period [us] after which we have received the UDP discover answer from emulator (-1 if emulator is connected over USB)
    uint32_t       HWVersion;                // J-Link via IP only: Hardware version of J-Link
    uint8_t        abMACAddr[6];             // J-Link via IP only: MAC Addr
    char      acProduct[32];            // Product name
    char      acNickName[32];           // J-Link via IP only: Nickname of J-Link
    char      acFWString[112];          // J-Link via IP only: Firmware string of J-Link
    char      IsDHCPAssignedIP;         // J-Link via IP only: Is J-Link configured for IP address reception via DHCP?
    char      IsDHCPAssignedIPIsValid;  // J-Link via IP only
    char      NumIPConnections;         // J-Link via IP only: Number of IP connections which are currently established to this J-Link
    char      NumIPConnectionsIsValid;  // J-Link via IP only
    uint8_t        aPadding[34];             // Pad struct size to 264 bytes
} JLINKARM_EMU_CONNECT_INFO;          // In general, unused fields are zeroed.

class JLinkOperations {
public:
    // 构造函数，加载 J-Link 动态库并初始化函数指针
    // JLinkOperations(const char* lib_path);
    JLinkOperations(const char *lib_Path, const char *deviceName);

    // 析构函数，关闭动态库
    ~JLinkOperations();

    // J-Link 初始化和连接
    bool initializeJLink();
    void close();

    // 内存读写功能
    // void readMemory(U32 targetAddr, U32 numBytes, U8* buffer);
    bool readMemory(unsigned int address, void *buffer, unsigned int numBytes);
    bool writeMemory(unsigned int address, const void *buffer, unsigned int numBytes);

    // 启动 RTT
    bool startRTT();

    // 停止 RTT
    void stopRTT();

    // RTT 读取
    int readRTT(int bufferIndex, void* buf, int numBytes);

    // RTT 写入
    int writeRTT(int bufferIndex, const void* buf, int numBytes);

    int ifConnected();
    int ifEmuConnected();
    bool isConnected();
    bool reInit();
    bool ifHaveUSB();

private:
    void* hJLinkDLL; // 动态库句柄

    // J-Link 函数指针
    typedef int (*JLINKARM_OPEN_FUNC)();
    typedef void (*JLINKARM_CLOSE_FUNC)();
    typedef int (*JLINKARM_TIF_SELECT_FUNC)(int);
    typedef int (*JLINKARM_CONNECT_FUNC)();
    typedef int (*JLINKARM_READMEM_FUNC)(U32 Addr, U32 NumBytes, void *pData);
    typedef void (*JLINKARM_SetSpeed_FUNC)(U32 Speed);
    typedef int (*JLINKARM_EXEC_COMMAND_FUNC)(unsigned char *, unsigned char *, int);
    typedef int (*JLINK_RTTERMINAL_CONTROL_FUNC)(int command, void *config);
    typedef int (*JLINK_RTTERMINAL_READ_FUNC)(int buffer_index, void *buf, int num_bytes);
    typedef int (*JLINK_RTTERMINAL_WRITE_FUNC)(int buffer_index, const void *buf, int num_bytes);
    typedef int (*JLINKARM_ISOPEN_FUNC)();
    typedef char (*JLINKARM_EMU_ISCONNECTED_FUNC)();
    typedef int (*JLINKARM_ISCONNECTED_FUNC)();
    typedef int (*JLINKARM_EMU_GETLIST_FUNC)(int, JLINKARM_EMU_CONNECT_INFO * paConnectInfo, int MaxInfos);
    typedef int (*JLINK_SetHOOK_DIALOG_UNSECURE_FUNC)(JLINK_HOOK_DIALOG_UNSECURE * pFunc);
    typedef void (*JLINKARM_SET_ERROR_OUT_HANDLE_FUNC)(JLINKARM_LOG* pfErrorOut);
    typedef void (*JLINKARM_SET_WARNING_OUT_HANDLE_FUNC)(JLINKARM_LOG* pfWarningOut);

    JLINKARM_OPEN_FUNC JLINKARM_Open;
    JLINKARM_CLOSE_FUNC JLINKARM_Close;
    JLINKARM_TIF_SELECT_FUNC JLINKARM_TIF_Select;
    JLINKARM_CONNECT_FUNC JLINKARM_Connect;
    JLINKARM_READMEM_FUNC JLINKARM_ReadMem;
    JLINKARM_SetSpeed_FUNC JLINKARM_SetSpeed;
    JLINKARM_EXEC_COMMAND_FUNC JLINKARM_ExecCommand;
    JLINK_RTTERMINAL_CONTROL_FUNC JLINK_RTTERMINAL_Control;
    JLINK_RTTERMINAL_READ_FUNC JLINK_RTTERMINAL_Read;
    JLINK_RTTERMINAL_WRITE_FUNC JLINK_RTTERMINAL_Write;
    JLINKARM_ISOPEN_FUNC JLINKARM_IsOpen;
    JLINKARM_EMU_ISCONNECTED_FUNC JLINKARM_EmuIsConnected;
    JLINKARM_ISCONNECTED_FUNC JLINKARM_IsConnected;
    JLINKARM_EMU_GETLIST_FUNC JLINKARM_EmuGetList;
    JLINK_SetHOOK_DIALOG_UNSECURE_FUNC JLINK_SetHOOK_Dialog_UNSECURE;
    JLINKARM_SET_ERROR_OUT_HANDLE_FUNC JLINKARM_SetErrorOutHandle;
    JLINKARM_SET_WARNING_OUT_HANDLE_FUNC JLINKARM_SetWarningOutHandle;

    const char *deviceName;

    char acIn[0x400];
    char acOut[0x400];

    JLINKARM_EMU_CONNECT_INFO connectInfo[2];
};

}
